#include "MySqlSelectQueryBlock.h"
#include "../../../../../utils/ifeq.h"
#include "../../../../../utils/instanceof.h"

#include "../../../../visitor/SQLASTVisitor.h"
#include "../../visitor/MySqlASTVisitor.h"
#include "../../../../SQLUtils.h"

MySqlSelectQueryBlock::MySqlSelectQueryBlock()
{
  dbType = DbType_ptr(new DbType(&DbType::mysql));
  procedureArgumentList = std::make_shared<std::list<SQLExpr_ptr>>();
}

SQLObject_ptr MySqlSelectQueryBlock::clone()
{
  SQLObject_ptr clone_tmp = std::shared_ptr<MySqlSelectQueryBlock>(new MySqlSelectQueryBlock());
  std::shared_ptr<MySqlSelectQueryBlock> x = std::dynamic_pointer_cast<MySqlSelectQueryBlock>(clone_tmp);
  // MySqlSelectQueryBlock *x = new MySqlSelectQueryBlock();
  cloneTo(x);

  x->hignPriority = hignPriority;
  x->straightJoin = straightJoin;

  x->smallResult = smallResult;
  x->bigResult = bigResult;
  x->bufferResult = bufferResult;
  x->cache = cache;
  x->calcFoundRows = calcFoundRows;

  if (procedureName != NULL)
  {
    x->setProcedureName(std::dynamic_pointer_cast<SQLName>(procedureName->clone()));
  }
  if (!procedureArgumentList->empty())
  {
    for (SQLExpr_ptr arg : *procedureArgumentList)
    {
      SQLExpr_ptr arg_cloned = std::dynamic_pointer_cast<SQLExpr>(arg->clone());
      arg_cloned->setParent(SharedObject(MySqlSelectQueryBlock));
      x->procedureArgumentList->push_back(arg_cloned);
    }
  }
  x->lockInShareMode = lockInShareMode;

  return x;
}

int MySqlSelectQueryBlock::getHintsSize()
{
  if (hints->empty())
  {
    return 0;
  }

  return hints->size();
}

BOOL_ptr MySqlSelectQueryBlock::isLockInShareMode()
{
  return lockInShareMode;
}

void MySqlSelectQueryBlock::setLockInShareMode(BOOL_ptr lockInShareMode)
{
  this->lockInShareMode = lockInShareMode;
}

SQLName_ptr MySqlSelectQueryBlock::getProcedureName()
{
  return procedureName;
}

void MySqlSelectQueryBlock::setProcedureName(SQLName_ptr procedureName)
{
  this->procedureName = procedureName;
}

SQLExpr_list_ptr  MySqlSelectQueryBlock::getProcedureArgumentList()
{
  // if (procedureArgumentList.empty())
  // {
  //   procedureArgumentList;
  // }
  return procedureArgumentList;
}

BOOL_ptr MySqlSelectQueryBlock::isHignPriority()
{
  return hignPriority;
}

void MySqlSelectQueryBlock::setHignPriority(BOOL_ptr hignPriority)
{
  this->hignPriority = hignPriority;
}

BOOL_ptr MySqlSelectQueryBlock::isStraightJoin()
{
  return straightJoin;
}

void MySqlSelectQueryBlock::setStraightJoin(BOOL_ptr straightJoin)
{
  this->straightJoin = straightJoin;
}

BOOL_ptr MySqlSelectQueryBlock::isSmallResult()
{
  return smallResult;
}

void MySqlSelectQueryBlock::setSmallResult(BOOL_ptr smallResult)
{
  this->smallResult = smallResult;
}

BOOL_ptr MySqlSelectQueryBlock::isBigResult()
{
  return bigResult;
}

void MySqlSelectQueryBlock::setBigResult(BOOL_ptr bigResult)
{
  this->bigResult = bigResult;
}

BOOL_ptr MySqlSelectQueryBlock::isBufferResult()
{
  return bufferResult;
}

void MySqlSelectQueryBlock::setBufferResult(BOOL_ptr bufferResult)
{
  this->bufferResult = bufferResult;
}

BOOL_ptr MySqlSelectQueryBlock::getCache()
{
  return cache;
}

void MySqlSelectQueryBlock::setCache(BOOL_ptr cache)
{
  this->cache = cache;
}

BOOL_ptr MySqlSelectQueryBlock::isCalcFoundRows()
{
  return calcFoundRows;
}

void MySqlSelectQueryBlock::setCalcFoundRows(BOOL_ptr calcFoundRows)
{
  this->calcFoundRows = calcFoundRows;
}

bool MySqlSelectQueryBlock::equals(Object_ptr o)
{
  if (this == o.get())
  {
    return true;
  }
  if (o == NULL || getClass() != o->getClass())
  {
    return false;
  }
  if (!SQLSelectQueryBlock::equals(o))
  {
    return false;
  }

  std::shared_ptr<MySqlSelectQueryBlock> that = std::dynamic_pointer_cast<MySqlSelectQueryBlock>(o);

  if (hignPriority != that->hignPriority)
  {
    return false;
  }
  if (straightJoin != that->straightJoin)
  {
    return false;
  }
  if (smallResult != that->smallResult)
  {
    return false;
  }
  if (bigResult != that->bigResult)
  {
    return false;
  }
  if (bufferResult != that->bufferResult)
  {
    return false;
  }
  if (calcFoundRows != that->calcFoundRows)
  {
    return false;
  }
  if (lockInShareMode != that->lockInShareMode)
  {
    return false;
  }
  if (!(cache == that->cache))
  {
    return false;
  }
  if (procedureName != NULL ? !(procedureName == that->procedureName) : that->procedureName != NULL)
  {
    return false;
  }
  if (!procedureArgumentList->empty() ? !IFEQ::ifeq<SQLExpr_ptr>(procedureArgumentList, that->procedureArgumentList) : !that->procedureArgumentList->empty())
  {
    return false;
  }
  if (!hints->empty() ? (hints == that->hints) : !that->hints->empty())
  {
    return false;
  }
  if (forcePartition != NULL ? !(forcePartition == that->forcePartition) : that->forcePartition != NULL)
  {
    return false;
  }

  return true;
}

int MySqlSelectQueryBlock::hashCode()
{
  int result = SQLSelectQueryBlock::hashCode();
  result = 31 * result + (hignPriority ? 1 : 0);
  result = 31 * result + (straightJoin ? 1 : 0);
  result = 31 * result + (smallResult ? 1 : 0);
  result = 31 * result + (bigResult ? 1 : 0);
  result = 31 * result + (bufferResult ? 1 : 0);
  // result = 31 * result + (cache != NULL ? cache.hashCode() : 0);
  result = 31 * result + (calcFoundRows ? 1 : 0);
  result = 31 * result + (procedureName != NULL ? procedureName->hashCode() : 0);
  // result = 31 * result + (procedureArgumentList != NULL ? procedureArgumentList.hashCode() : 0);
  result = 31 * result + (lockInShareMode ? 1 : 0);
  // result = 31 * result + (hints != NULL ? hints.hashCode() : 0);
  result = 31 * result + (forcePartition != NULL ? forcePartition->hashCode() : 0);
  return result;
}

void MySqlSelectQueryBlock::accept0(SQLASTVisitor_ptr visitor)
{
  if (instanceof <SQLASTVisitor, MySqlASTVisitor>(visitor.get()))
  {
    MySqlASTVisitor_ptr tmp = std::dynamic_pointer_cast<MySqlASTVisitor>(visitor);
    accept0(tmp);
    return;
  }

  SQLSelectQueryBlock::accept0(visitor);
}

void MySqlSelectQueryBlock::accept0(MySqlASTVisitor_ptr visitor)
{
  if (visitor->visit(SharedObject(MySqlSelectQueryBlock)))
  {
    auto it_selectList = this->selectList->begin();
    for (int i = 0; i < this->selectList->size(); i++)
    {
      std::advance(it_selectList, i);
      auto item = (*it_selectList);
      if (item != NULL)
      {
        item->accept(visitor);
      }
    }

    if (this->from != NULL)
    {
      this->from->accept(visitor);
    }

    if (this->into != NULL)
    {
      this->into->accept(visitor);
    }

    if (this->where != NULL)
    {
      this->where->accept(visitor);
    }

    if (this->startWith != NULL)
    {
      this->startWith->accept(visitor);
    }

    if (this->connectBy != NULL)
    {
      this->connectBy->accept(visitor);
    }

    if (this->groupBy != NULL)
    {
      this->groupBy->accept(visitor);
    }

    if (!this->windows->empty())
    {
      for (SQLWindow_ptr item : *windows)
      {
        item->accept(visitor);
      }
    }

    if (this->orderBy != NULL)
    {
      this->orderBy->accept(visitor);
    }

    if (!this->distributeBy->empty())
    {
      auto it_distributeBy = distributeBy->begin();
      for (int i = 0; i < distributeBy->size(); i++)
      {
        std::advance(it_distributeBy, i);
        SQLSelectOrderByItem_ptr item = (*it_distributeBy);
        item->accept(visitor);
      }
    }

    if (!this->sortBy->empty())
    {
      auto it_sortBy = sortBy->begin();
      for (int i = 0; i < sortBy->size(); i++)
      {
        std::advance(it_sortBy, i);
        SQLSelectOrderByItem_ptr item = (*it_sortBy);
        item->accept(visitor);
      }
    }

    if (this->waitTime != NULL)
    {
      this->waitTime->accept(visitor);
    }

    if (this->limit_ != NULL)
    {
      this->limit_->accept(visitor);
    }

    if (this->procedureName != NULL)
    {
      this->procedureName->accept(visitor);
    }

    if (!this->procedureArgumentList->empty())
    {
      for (SQLExpr_ptr item : *procedureArgumentList)
      {
        item->accept(visitor);
      }
    }
  }

  visitor->endVisit(SharedObject(MySqlSelectQueryBlock));
}

SQLName_ptr MySqlSelectQueryBlock::getForcePartition()
{
  return forcePartition;
}

void MySqlSelectQueryBlock::setForcePartition(SQLName_ptr x)
{
  if (x != NULL)
  {
    x->setParent(SharedObject(MySqlSelectQueryBlock));
  }
  this->forcePartition = x;
}

string_ptr  MySqlSelectQueryBlock::toString()
{
  string_ptr  tmp = SQLUtils::toMySqlString(SharedObject(MySqlSelectQueryBlock));
  return (tmp);
}
